
package com.ezlcp.user.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ezlcp.commons.base.db.BaseDao;
import com.ezlcp.commons.base.db.BaseService;
import com.ezlcp.commons.base.entity.JsonResult;
import com.ezlcp.commons.exception.GetLockTimeoutException;
import com.ezlcp.commons.service.impl.SuperServiceImpl;
import com.ezlcp.commons.tool.IdGenerator;
import com.ezlcp.commons.tool.StringUtils;
import com.ezlcp.commons.utils.ContextUtil;
import com.ezlcp.user.entity.DicValue;
import com.ezlcp.user.entity.SerialNo;
import com.ezlcp.user.mapper.SerialNoMapper;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;


/**
 * [單據編號，系统初始化公司ID为空的记录，每个公司创建后从初始记录复制]业务服务类
 */
@Service
public class SerialNoServiceImpl extends SuperServiceImpl<SerialNoMapper, SerialNo> implements BaseService<SerialNo> {
    @Autowired
    private SerialNoMapper serialNoMapper;
    @Resource
    RedisLockRegistry redisLockRegistry;
    @Resource
    private LogService logService;
    @Resource
    private DicValueServiceImpl dicValueService;

    /**
     * redis锁KEY前缀
     */
    private static final String LOCK_KEY = "LOCK_SN:";

    /**
     * @param orderType 单据类型，值为以下英文：
     * @return java.lang.String
     * @description: 获取单据编号
     * @author Elwin ZHANG
     * @date 2022/5/23 14:03
     */
    public String getSerialNo(String orderType) {
        String tenantId = ContextUtil.getCurrentTenantId();
        if (StringUtils.isEmpty(tenantId)) {
            return "";
        }
        String no = genSerialNo(orderType, tenantId);
        return  no;
    }

    /**
     * @param orderType 单据类型
     * @param tenantId  公司ID
     * @return java.lang.String 生成的编号
     * @description: 根据单据类型和公司ID生成编号
     * @author Elwin ZHANG
     * @date 2022/5/20 17:45
     */
    private String genSerialNo(String orderType, String tenantId) {
        String key = LOCK_KEY + tenantId + ":" + orderType;
        Lock lock = redisLockRegistry.obtain(key);
        boolean rtn = false;
        try {
            rtn = lock.tryLock(5, TimeUnit.SECONDS);
            if (rtn) {
                try {
                    return generateNo(orderType, tenantId);
                }finally {
                    lock.unlock();
                }
            }else {
                throw new GetLockTimeoutException(orderType);
            }
        } catch (InterruptedException e) {
            throw new GetLockTimeoutException(orderType);
        }
    }

    /**
     * @param serialNo 校验规则
     * @return com.hkyctech.commons.base.entity.JsonResult
     * @author Elwin ZHANG
     * @date 2022/5/24 15:31
     */
    public JsonResult validRule(SerialNo serialNo) {
        String rules = serialNo.getRules();
        short length = serialNo.getNoLen();
        int resetType = serialNo.getResetType();
        if (StringUtils.isEmpty(rules)) {
            return JsonResult.Fail("seriaNo.ruleNotNull");
        }
        if (length < 3) {
            return JsonResult.Fail("seriaNo.runningAccountLength");
        }
        if (!rules.contains("{NO}") && !rules.contains("{no}")) {
            return JsonResult.Fail("seriaNo.runningAccountNO");
        }
        switch (resetType) {
            case 1: {
                if (!rules.contains("DD}") && !rules.contains("dd}")) {
                    return JsonResult.Fail("seriaNo.resetTypeDay");
                }
                break;
            }
            case 2: {
                if (!rules.contains("MM}") && !rules.contains("mm}")) {
                    return JsonResult.Fail("seriaNo.resetTypeMonth");
                }
                break;
            }
            case 4: {
                if (!rules.contains("yy}")) {
                    return JsonResult.Fail("seriaNo.resetTypeYear");
                }
                break;
            }
            default:
                break;
        }
        String no = getByRule(rules, length, 999);
        return JsonResult.getSuccessResult(no, "seriaNo.resetSuccess");
    }

    /**
     * 根据规则返回需要显示的单据编号
     *
     * @param rule     单据编号 规则。
     * @param length   流水号的长度。
     * @param curValue 流水号的当前值。
     * @return 单据编号
     */
    private String getByRule(String rule, short length, long curValue) {
        Calendar now = Calendar.getInstance();
        NumberFormat nf = new DecimalFormat("00");
        int year = now.get(Calendar.YEAR);
        int month = now.get(Calendar.MONTH) + 1;
        int day = now.get(Calendar.DATE);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append("0");
        }
        SimpleDateFormat fullDateFormat = new SimpleDateFormat("yyyyMMdd");
        SimpleDateFormat shortDateFormat = new SimpleDateFormat("yyyMM");
        NumberFormat seqFt = new DecimalFormat(sb.toString());
        String seqNo = seqFt.format(curValue);
        return rule.replace("{yyyy}", year + "")
                .replace("{yy}", String.valueOf(year).substring(2, 4))
                .replace("{MM}", nf.format(month))
                .replace("{mm}", month + "")
                .replace("{DD}", nf.format(day))
                .replace("{dd}", day + "")
                .replace("{NO}", seqNo)
                .replace("{no}", curValue + "")
                .replace("{yyyyMM}", shortDateFormat.format(now.getTime()))
                .replace("{yyyyMMDD}", fullDateFormat.format(now.getTime()));
    }

    /***
     * @description: 获取某公司的某个单擗编号配置
     * @param orderType 单据类型
     * @param tenantId 公司ID
     * @return com.hkyctech.loan.core.entity.SerialNo
     * @author Elwin ZHANG
     * @date 2022/5/20 18:15
     */
    public SerialNo getByOrderType(String orderType, String tenantId) {
        QueryWrapper<SerialNo> wrapper = new QueryWrapper<>();
        wrapper.eq("order_type", orderType);
        wrapper.eq("tenant_id", tenantId);
        return serialNoMapper.selectOne(wrapper);
    }

    /**
     * @param tenantId 公司ID
     * @return java.util.List<com.hkyctech.loan.core.entity.SerialNo>
     * @description: 获取某公司的全部单据编号配置，如果没有记录，则从系统默认配置中复制一份
     * @author Elwin ZHANG
     * @date 2022/5/20 18:18
     */
    @Transactional(rollbackFor = Exception.class)
    public List<SerialNo> getByTenant(String tenantId) {
        QueryWrapper<SerialNo> wrapper = new QueryWrapper<>();
        wrapper.eq("tenant_id", tenantId);
        List<SerialNo> list = serialNoMapper.selectList(wrapper);
        //如果取到值，则直接返回
        if (list != null && list.size() > 0) {
            return list;
        }
        //获取默认值
        list = getDefaultSet();
        if (list == null || list.size() == 0) {
            throw new RuntimeException("數據庫中沒有初始化默認的單據編號");
        }
        //复制系统默认配置
        for (SerialNo sn : list) {
            sn.setId(IdGenerator.getIdStr());
            sn.setTenantId(tenantId);
            sn.setSeq(1);
            serialNoMapper.insert(sn);
            logService.saveSystemLog("公司默認的單據編號赋值", "/registerCompany", sn.getPkId(), JSON.toJSONString(sn));
        }
        return list;
    }

    /**
     * @return java.util.List<com.hkyctech.loan.core.entity.SerialNo>
     * @description: 获取默认的全部单据编号配置
     * @author Elwin ZHANG
     * @date 2022/5/20 18:23
     */
    public List<SerialNo> getDefaultSet() {
        QueryWrapper<SerialNo> wrapper = new QueryWrapper<>();
        wrapper.and(query -> query.eq("tenant_id", "").or().isNull("tenant_id"));
        return serialNoMapper.selectList(wrapper);
    }

    /**
     * @param orderType 单据类型
     * @param tenantId  公司ID
     * @return java.lang.String
     * @description: 生成单号
     * @author Elwin ZHANG
     * @date 2022/5/20 18:24
     */
    private String generateNo(String orderType, String tenantId) {
        SerialNo sn = getByOrderType(orderType, tenantId);
        //取查到当前公司的指定单据类型
        if (sn == null) {
            return "seriaNo.receiptsType";
        }
        String rule = sn.getRules();
        Long curValue = sn.getCurrentVal();

        if (curValue == null) {
            curValue = (long) sn.getInitVal();
        }
        curValue += sn.getStep();
        sn.setCurrentVal(curValue);
        this.update(sn);
        return getByRule(rule, sn.getNoLen(), curValue);
    }

    /**
     * @param array 单据编号JSON数组
     * @description: 批量保存单据编号
     * @author Elwin ZHANG
     * @date 2022/5/23 10:09
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void batchUpdate(JSONArray array) {
        //批量更新
        for (int i = 0; i < array.size(); i++) {
            JSONObject object = array.getJSONObject(i);
            String str = JSON.toJSONString(object);
            SerialNo sn = JSON.parseObject(str, SerialNo.class);
            Integer seq = sn.getSeq();
            if (seq == null) {
                seq = 1;
            }
            sn.setSeq(seq + 1);
            serialNoMapper.updateById(sn);
        }
    }
    @Override
    public BaseDao<SerialNo> getRepository() {
        return serialNoMapper;
    }
}